﻿/**
 * Copyright (C) 2013 original author and authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.Core.Interceptor;
using System.Reflection;
using NGuice.Inject.Spi;
using AopAlliance.Intercept;
using NGuice.Inject.Internal.Util;
using Castle.DynamicProxy;
using CGLib;

namespace NGuice.Inject.Internal
{
    //completely！
    /// <summary>
    /// 构建一个构造代理，此代理能够参与AOP。此类管理应用的类型和方法，这些类型和方法由被拦截的方法集合组成
    /// <remarks>
    /// 此类使用Castle DynamicProxy管理代理
    /// </remarks>
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class ProxyFactory<T> : ConstructionProxyFactory<T>
    {
        private static readonly Castle.Core.Interceptor.IInterceptor NO_OP_METHOD_INTERCEPTOR = new NoOpInterceptor();
        private readonly InjectionPoint injectionPoint;

        //AOP统一方法拦截接口
        private readonly IDictionary<MethodInfo, IList<IMethodInterceptor>> interceptors;

        private readonly Type declaringClass;
        private readonly IList<MethodInfo> methods;

        //Castle DynamicProxy方法拦截接口
        private readonly Castle.Core.Interceptor.IInterceptor[] callbacks;

        internal ProxyFactory(InjectionPoint injectionPoint, IEnumerable<MethodAspect> methodAspects)
        {
            this.injectionPoint = injectionPoint;

            // the member of injectionPoint is always a Constructor<T>
            ConstructorInfo constructor = (ConstructorInfo)injectionPoint.Member;
            declaringClass = constructor.DeclaringType;

            // Find applicable aspects. Bow out if none are applicable to this class.
            List<MethodAspect> applicableAspects = Lists.NewArrayList<MethodAspect>();
            foreach (MethodAspect methodAspect in methodAspects)
            {
                if (methodAspect.Matches(declaringClass))
                {
                    applicableAspects.Add(methodAspect);
                }
            }

            if (applicableAspects.Count == 0)
            {
                interceptors = ImmutableDictionary<MethodInfo, IList<IMethodInterceptor>>.Of();
                methods = ImmutableList<MethodInfo>.Of();
                callbacks = null;
                return;
            }

            // Get list of methods from cglib.
            methods = Lists.NewArrayList<MethodInfo>();
            //Enhancer.getMethods(declaringClass, null, methods);
            foreach (var m in declaringClass.GetMethods())
            {
                methods.Add(m);
            }


            // Create method/interceptor holders and record indices.
            List<MethodInterceptorsPair> methodInterceptorsPairs = Lists.NewArrayList<MethodInterceptorsPair>();
            foreach (MethodInfo method in methods)
            {
                methodInterceptorsPairs.Add(new MethodInterceptorsPair(method));
            }

            //迭代方法的切面并为这些方法添加应用的拦截器
            bool anyMatched = false;
            foreach (MethodAspect methodAspect in applicableAspects)
            {
                foreach (MethodInterceptorsPair pair in methodInterceptorsPairs)
                {
                    if (methodAspect.Matches(pair.method))
                    {
                        //visibility = visibility.and(BytecodeGen.Visibility.forMember(pair.method));
                        pair.AddAll(methodAspect.Interceptors);
                        anyMatched = true;
                    }
                }
            }

            if (!anyMatched)
            {
                interceptors = ImmutableDictionary<MethodInfo, IList<IMethodInterceptor>>.Of();
                callbacks = null;
                return;
            }

            ImmutableDictionaryBuilder<MethodInfo, IList<IMethodInterceptor>> interceptorsMapBuilder = null; // lazy

            callbacks = new Castle.Core.Interceptor.IInterceptor[methods.Count];
            for (int i = 0; i < methods.Count; i++)
            {
                MethodInterceptorsPair pair = methodInterceptorsPairs[i];

                if (!pair.HasInterceptors())
                {
                    callbacks[i] = NO_OP_METHOD_INTERCEPTOR;
                    continue;
                }

                if (interceptorsMapBuilder == null)
                {
                    interceptorsMapBuilder = ImmutableDictionary<MethodInfo, IList<IMethodInterceptor>>.Builder();
                }

                interceptorsMapBuilder.Put(pair.method,
                    ImmutableList<IMethodInterceptor>.CopyOf<IMethodInterceptor>(pair.interceptors));
                callbacks[i] = new InterceptorStackCallback(pair.method, pair.interceptors);
            }

            interceptors = interceptorsMapBuilder != null
                ? interceptorsMapBuilder.Build() as IDictionary<MethodInfo, IList<IMethodInterceptor>>
                : ImmutableDictionary<MethodInfo, IList<IMethodInterceptor>>.Of() as IDictionary<MethodInfo, IList<IMethodInterceptor>>;
        }

        public IDictionary<MethodInfo, IList<IMethodInterceptor>> Interceptors
        {
            get { return interceptors; }
        }

        public ConstructionProxy<T> Create()
        {
            if (interceptors.Count == 0)
            {
                return new DefaultConstructionProxyFactory<T>(injectionPoint).Create();
            }

            Type[] callbackTypes = new Type[methods.Count];
            for (int i = 0, len = callbackTypes.Length; i < len; i++)
                callbackTypes[i] = typeof(Castle.Core.Interceptor.IInterceptor);
            // Create the proxied class. We're careful to ensure that all enhancer state is not-specific
            // to this injector. Otherwise, the proxies for each injector will waste PermGen memory
            try
            {
                Enhancer enhancer = new Enhancer(declaringClass);
                enhancer.CallbackFilter = new IndicesCallbackFilter(declaringClass, methods);
                //enhancer.setCallbackTypes(callbackTypes);
                return new ProxyConstructor<T>(enhancer, injectionPoint, callbacks, interceptors);
            }
            catch (Exception e)
            {
                throw new Errors().ErrorEnhancingClass(declaringClass, e).ToException();
            }
        }
    }

    internal class NoOpInterceptor : Castle.Core.Interceptor.IInterceptor
    {
        public void Intercept(Castle.Core.Interceptor.IInvocation invocation)
        {
            invocation.Proceed();
        }
    }

    internal class MethodInterceptorsPair
    {
        internal readonly MethodInfo method;
        internal List<IMethodInterceptor> interceptors; // lazy

        public MethodInterceptorsPair(MethodInfo method)
        {
            this.method = method;
        }

        internal void AddAll(IList<IMethodInterceptor> interceptors)
        {
            if (this.interceptors == null)
            {
                this.interceptors = Lists.NewArrayList<IMethodInterceptor>();
            }
            this.interceptors.AddRange(interceptors);
        }

        internal bool HasInterceptors()
        {
            return interceptors != null;
        }
    }

    /// <summary>
    /// 拦截器的过滤器，他映射方法到唯一ID，我们使用声明的类定义Equals和GetHashCode，
    /// 以便代理的类能够在注入器之间共享
    /// </summary>
    internal class IndicesCallbackFilter : IInterceptorSelector
    {

        readonly Type declaringClass;
        readonly IDictionary<MethodInfo, int?> indices;

        internal IndicesCallbackFilter(Type declaringClass, IList<MethodInfo> methods)
        {
            this.declaringClass = declaringClass;
            IDictionary<MethodInfo, int?> indices = new Dictionary<MethodInfo, int?>();
            for (int i = 0; i < methods.Count; i++)
            {
                MethodInfo method = methods[i];
                indices.Add(method, i);
            }
            this.indices = indices;
        }
        public Castle.Core.Interceptor.IInterceptor[] SelectInterceptors(Type type, MethodInfo method, Castle.Core.Interceptor.IInterceptor[] interceptors)
        {
            int? i = indices[method];
            if (i == null)
                return new Castle.Core.Interceptor.IInterceptor[0];
            else
                return new Castle.Core.Interceptor.IInterceptor[] { interceptors[(int)i] };
        }

        //public int accept(Method method) {
        //  return indices.get(method);
        //}

        public override bool Equals(object o)
        {
            return o is IndicesCallbackFilter &&
                ((IndicesCallbackFilter)o).declaringClass == declaringClass;
        }

        public override int GetHashCode()
        {
            return declaringClass.GetHashCode();
        }

    }

    /// <summary>
    /// 构造在AOP中参与的实例
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class ProxyConstructor<T> : ConstructionProxy<T>
    {
        readonly Enhancer enhanced;
        readonly InjectionPoint injectionPoint;
        readonly ConstructorInfo constructor;
        readonly Castle.Core.Interceptor.IInterceptor[] callbacks;

        //readonly FastConstructor fastConstructor;
        readonly IDictionary<MethodInfo, IList<IMethodInterceptor>> methodInterceptors;
        readonly ProxyGenerator generator;

        // the constructor promises to construct 'T's
        internal ProxyConstructor(Enhancer enhanced, InjectionPoint injectionPoint, Castle.Core.Interceptor.IInterceptor[] callbacks,
            IDictionary<MethodInfo, IList<IMethodInterceptor>> methodInterceptors)
        {
            //改成Castle DynamicProxy实现
            this.enhanced = enhanced; // this returns a cached class if possible
            this.injectionPoint = injectionPoint;
            //this.constructor = (ConstructorInfo)injectionPoint.Member;
            this.callbacks = callbacks;
            this.methodInterceptors = methodInterceptors;
            this.generator = new ProxyGenerator();

            //FastClass fastClass = BytecodeGen.NewFastClass(enhanced.DeclaringClass);
            //ParameterInfo[] parames = constructor.GetParameters();
            //Type[] types = new Type[parames.Length];
            //for (int i = 0; i < types.Length; i++) {
            //    types[i] = parames[i].ParameterType;
            //}
            ////this.fastConstructor = fastClass.GetConstructor(types);
        }


        public T NewInstance(params object[] arguments)
        {
            ProxyGenerationOptions options = new ProxyGenerationOptions();
            options.Selector = enhanced.CallbackFilter;
            return (T)generator.CreateClassProxy(enhanced.DeclaringClass, null, options, arguments, this.callbacks);
            //Enhancer.registerCallbacks(enhanced, callbacks);
            //try
            //{
            //    return (T)fastConstructor.newInstance(arguments);
            //}
            //finally
            //{
            //    Enhancer.registerCallbacks(enhanced, null);
            //}
        }

        public InjectionPoint InjectionPoint
        {
            get { return injectionPoint; }
        }

        public ConstructorInfo Constructor
        {
            get { return constructor; }
        }

        public IDictionary<MethodInfo, IList<IMethodInterceptor>> MethodInterceptors
        {
            get { return methodInterceptors; }
        }

    }
}
